#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <sys/time.h>
 
#include "data_type.h"
#include "cube.h"
#include "cube_define.h"
#include "cube_record.h"

#include "user_define.h"
#include "action_define.h"
#include "TAC_conn_struct.h"

// add para lib_include

char Buf[DIGEST_SIZE*4];

int AC_client_init(void * sub_proc, void * para)
{
	int ret;
	RECORD(TAC_DEFINE,CONN_STATE) * local_state;
	// add yorself's module init func here
	local_state=Dalloc0(sizeof(*local_state),NULL);
	if(local_state==NULL)
		return -ENOMEM;

    local_state->type=TAC_NODE_AC;
    local_state->state=TAC_STATE_INIT;
	proc_share_data_getvalue("uuid",local_state->node_uuid);	
	proc_share_data_getvalue("proc_name",local_state->proc_name);	
    
	ex_module_setpointer(sub_proc,local_state);	
	return 0;
}

int AC_client_start(void * sub_proc, void * para)
{
	int ret;
	void * recv_msg;
	int type;
	int subtype;
	RECORD(TAC_DEFINE,CONN_STATE) * client_state;
	// add yorself's module exec func here
	while(1)
	{
		usleep(time_val.tv_usec);
		ret=ex_module_recvmsg(sub_proc,&recv_msg);
		if(ret<0)
			continue;
		if(recv_msg==NULL)
			continue;
		type=message_get_type(recv_msg);
		subtype=message_get_subtype(recv_msg);
		if(!memdb_find_recordtype(type,subtype))
		{
			printf("message format (%d %d) is not registered!\n",
			message_get_type(recv_msg),message_get_subtype(recv_msg));
			continue;
		}
		client_state = ex_module_getpointer(sub_proc);	
		if(client_state->state == TAC_STATE_INIT)
		{
			if((type==TYPE(TAC_DEFINE))&&(subtype==SUBTYPE(TAC_DEFINE,LOGIN_USER)))
			{
				ret=proc_AC_remotelogin(sub_proc,recv_msg);
			}
        }
		else if(client_state->state == TAC_STATE_REQUEST)
        {
            if((type==TYPE(TAC_DEFINE))&&(subtype==SUBTYPE(TAC_DEFINE,LOGIN_CHALLENGE)))
			{
				ret=proc_AC_remoteresponse(sub_proc,recv_msg);
			}
        }
		else if(client_state->state == TAC_STATE_CHALLENGE)
        {
            if((type==TYPE(TAC_DEFINE))&&(subtype==SUBTYPE(TAC_DEFINE,LOGIN_RETURN)))
			{
				ret=proc_AC_remotereturn(sub_proc,recv_msg);
			}
            if(client_state->state==TAC_STATE_CLIENTVERIFY)
            {
                ret=proc_AC_sessionkeysend(sub_proc,NULL);
            }
		}
        else
        {
            if((type==TYPE(TAC_DEFINE))&&(subtype==SUBTYPE(TAC_DEFINE,ACTION)))
			{
				ret=proc_AC_action(sub_proc,recv_msg);
			}
            switch(client_state->state)
            {
                case TAC_STATE_SYMMKEYREADY:
                    ret=proc_AC_platformreport(sub_proc,NULL);
                    break;
                case TAC_STATE_CLIENTTRUST:
                    break;
                default:
                    return -EINVAL;
                    
            }
            
        }
	}
	return 0;
}

int proc_AC_remotelogin(void * sub_proc,void * recv_msg)
{
	int ret;
	RECORD(TAC_DEFINE,CONN_STATE) * conn_state;
	RECORD(TAC_DEFINE,LOGIN_USER) * login_user;
	void * new_msg;

	ret=message_get_record(recv_msg,&login_user,0);
	if(ret<0)
		return ret;

	conn_state = ex_module_getpointer(sub_proc);
	Memset(conn_state->nonce,0,DIGEST_SIZE);	
	Memcpy(login_user->uuid,conn_state->node_uuid,DIGEST_SIZE);
	
    if(login_user->user[0]==0)
	    proc_share_data_getvalue("user_name",login_user->user);

    Strncpy(conn_state->user,login_user->user,DIGEST_SIZE);    
    calculate_context_sm3(login_user->passwd,DIGEST_SIZE,conn_state->passwd);

	Memset(login_user->passwd,0,DIGEST_SIZE);

	ret=proc_share_data_getvalue("proc_name",login_user->proc_name);

	new_msg=message_create(TYPE_PAIR(TAC_DEFINE,LOGIN_USER),NULL);	
	if(new_msg==NULL)
		return -EINVAL;
	ret=message_add_record(new_msg,login_user);
	if(ret<0)
		return ret;
	
	ret=ex_module_sendmsg(sub_proc,new_msg);

	if(ret >=0)
		conn_state->state=TAC_STATE_REQUEST;
	return ret;
}

int proc_AC_remoteresponse(void * sub_proc,void * recv_msg)
{
	int ret;
	RECORD(TAC_DEFINE,CONN_STATE) * conn_state;
	RECORD(TAC_DEFINE,LOGIN_CHALLENGE) * login_info;
	RECORD(TAC_DEFINE,LOGIN_USER) * login_response;
	void * new_msg;
	
	conn_state = ex_module_getpointer(sub_proc);	
    if(conn_state==NULL)
        return -EINVAL;
	ret=message_get_record(recv_msg,&login_info,0);
	if(ret<0)
		return ret;
	if(login_info->retval != TAC_ACTION_CHALLENGE)
	{
		ret=ex_module_sendmsg(sub_proc,recv_msg);
		conn_state->state=TAC_STATE_FAIL;
		return 0;
	}
    login_response=Talloc0(sizeof(*login_response));

	Memcpy(login_response->uuid,conn_state->node_uuid,DIGEST_SIZE);

    Strncpy(login_response->user,conn_state->user,DIGEST_SIZE);    
	Memset(login_response->passwd,0,DIGEST_SIZE);

	ret=proc_share_data_getvalue("proc_name",login_response->proc_name);


	Memcpy(Buf,login_info->nonce,DIGEST_SIZE);
	Memcpy(Buf+DIGEST_SIZE,conn_state->passwd,DIGEST_SIZE);
	calculate_context_sm3(Buf,DIGEST_SIZE*2,login_response->passwd);

	new_msg=message_create(TYPE_PAIR(TAC_DEFINE,LOGIN_USER),NULL);	
	if(new_msg==NULL)
		return -EINVAL;
	ret=message_add_record(new_msg,login_response);
	if(ret<0)
		return ret;
	ret=ex_module_sendmsg(sub_proc,new_msg);

	if(ret >=0)
		conn_state->state=TAC_STATE_CHALLENGE;
	return ret;
}

int proc_AC_remotereturn(void * sub_proc,void * recv_msg)
{
	int ret;
	RECORD(TAC_DEFINE,CONN_STATE) * conn_state;
	RECORD(TAC_DEFINE,LOGIN_RETURN) * return_info;
	void * new_msg;
	
	ret=message_get_record(recv_msg,&return_info,0);
	if(ret<0)
		return ret;

	conn_state = ex_module_getpointer(sub_proc);

	if(return_info->return_code == SUCCEED)
	{
		print_cubeaudit("user %s login succeed!",conn_state->user);
		conn_state->state=TAC_STATE_CLIENTVERIFY;
	}
	else
	{
		print_cubeerr("user %s login failed!",conn_state->user);
		conn_state->state=TAC_STATE_INIT;
	}

	ret=ex_module_sendmsg(sub_proc,recv_msg);

	//proc_share_data_setpointer(client_state);

	return ret;
}

int proc_AC_sessionkeysend(void * sub_proc,void * recv_msg)
{
	int ret;
	RECORD(TAC_DEFINE,CONN_STATE) * conn_state;
	RECORD(TAC_DEFINE,ACTION) * TAC_action;
	void * new_msg;
	
	conn_state = ex_module_getpointer(sub_proc);	
    if(conn_state==NULL)
        return -EINVAL;
        
    TAC_action=Talloc0(sizeof(*TAC_action));

	Memcpy(TAC_action->uuid,conn_state->node_uuid,DIGEST_SIZE);

    Strncpy(TAC_action->user,conn_state->user,DIGEST_SIZE);    
    TAC_action->action=TAC_ACTION_SESSIONKEYSEND;
    RAND_bytes(TAC_action->nonce,32);

//	ret=proc_share_data_getvalue("proc_name",TAC_action->proc_name);


	new_msg=message_create(TYPE_PAIR(TAC_DEFINE,ACTION),NULL);	
	if(new_msg==NULL)
		return -EINVAL;
	ret=message_add_record(new_msg,TAC_action);
	if(ret<0)
		return ret;
	ret=ex_module_sendmsg(sub_proc,new_msg);

	if(ret >=0)
		conn_state->state=TAC_STATE_SYMMKEYREADY;
	return ret;
}

int proc_AC_action(void * sub_proc,void * recv_msg)
{
	int ret;
	RECORD(TAC_DEFINE,CONN_STATE) * conn_state;
	RECORD(TAC_DEFINE,ACTION) * TAC_action;
	RECORD(TAC_DEFINE,LOGIN_RETURN) * return_info;
	void * new_msg;
    char err_msg[32];
	
	conn_state = ex_module_getpointer(sub_proc);	
    if(conn_state==NULL)
        return -EINVAL;

	ret=message_get_record(recv_msg,&TAC_action,0);
	if(ret<0)
		return ret;
        

    switch(TAC_action->action)
    {
        case TAC_ACTION_SESSIONKEYGET:
		    conn_state->state=TAC_STATE_SYMMKEYREADY;
            break;
        case TAC_ACTION_PLATFORMVERIFY:
		    conn_state->state=TAC_STATE_SERVERTRUST;
            break;
        case TAC_ACTION_POLICYSEND:
        case TAC_ACTION_POLICYUPDATE:
		    conn_state->state=TAC_STATE_TRUST;
            break;

        default:
            conn_state->state=TAC_STATE_FAIL;
            break;
    }

    return 0;
}

int proc_AC_platformreport(void * sub_proc,void * recv_msg)
{
	int ret;
	RECORD(TAC_DEFINE,CONN_STATE) * conn_state;
	RECORD(TAC_DEFINE,ACTION) * TAC_action;
	void * new_msg;
	
	conn_state = ex_module_getpointer(sub_proc);	
    if(conn_state==NULL)
        return -EINVAL;
        
    TAC_action=Talloc0(sizeof(*TAC_action));

	Memcpy(TAC_action->uuid,conn_state->node_uuid,DIGEST_SIZE);

    Strncpy(TAC_action->user,conn_state->user,DIGEST_SIZE);    
    TAC_action->action=TAC_ACTION_PLATFORMREPORT;
    RAND_bytes(TAC_action->nonce,32);

//	ret=proc_share_data_getvalue("proc_name",TAC_action->proc_name);


	new_msg=message_create(TYPE_PAIR(TAC_DEFINE,ACTION),NULL);	
	if(new_msg==NULL)
		return -EINVAL;
	ret=message_add_record(new_msg,TAC_action);
	if(ret<0)
		return ret;
	ret=ex_module_sendmsg(sub_proc,new_msg);

	if(ret >=0)
		conn_state->state=TAC_STATE_SERVERVERIFY;
	return ret;
}
